package main

import "fmt"

func printSlice(s []int) {
	fmt.Printf("%v,len=%d,cap=%d\n", s, len(s), cap(s))
}

func main() {
	/**
	向slice添加元素
	-  由于go语言只有值传递的关系，必须接受append的返回值(因为原slice的ptr等值都可能被改变)
	- 添加元素时如果没超越cap，则会修改cap内的底层array对应的元素值
	- 如果超越了cap，系统会重新分配更大的底层数组（slice底层的ptr、cap都会改变、len可能会改变），并且原来的元素就会拷贝到新的这个更大的底层数组
	- 那原来的那个array咋办？go语言是有垃圾回收机制的，原来的arr如果还有人用，它就还会在，如果没人用呢，那原来的数组会被垃圾回收掉
	- 语法：s = append(s, val)
	*/
	/*
		arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
		s1 := arr[2:6]       // [2,3,4,5]
		s2 := s1[3:5]        // [5,6]
		s3 := append(s2, 10) // s2里面添加10之后，s1是看arr的，所以底层arr也被改变了，7被修改为了10
		//fmt.Println(s3)      // [5 6 10]
		//fmt.Println(arr)     // [0 1 2 3 4 5 6 10]

		s4 := append(s3, 11)
		fmt.Println(s3)
		fmt.Println(s4)  // [5,6,10,11] // 加的11，超出了底层arr的长度，即s3的cap，所以底层的arr是不变的，但是作为view层的s3添加
		fmt.Println(arr) // 还是 [0 1 2 3 4 5 6 10]

		//s5 := append(s4, 12)
		//fmt.Println(s5)
		//fmt.Println(arr) // 还是 [0 1 2 3 4 5 6 10]

		// 但是，s4和s5里面的11和12，是go语言内部开辟了一个新cap的array，即 s4 and s5 view different array
		// 或者说，s4 and s5 no longer view arr. ，即 s4和s5不再是对arr的一个view，那是view谁呢，我们不需要知道，go内部分配了一个更加长的一个array，来填下我们更多的一个数据
	*/

	/*
		- slice可以不一定要定义于一个数组上，还可以单独定义
		- go语言的类型里面，每个类型都会有一个零值（Zero Value），而slice的零值nil
		- slice创建的cap容量会随内容变多而变大
	*/
	// slice的创建方式二：
	/*
		var s []int
		fmt.Println(s)        // []
		fmt.Println(s == nil) // true
		//for k,v range s{}
		for i := 0; i < 100; i++ {
			printSlice(s) // cap可以当作一个容器长度，一开始go预分配0，1，2，4，8，16，32，64，128...
			s = append(s, 2*i+1)
		}
		fmt.Println(s)
	*/

	// slice的创建方式三：
	/*
		s1 := []int{2, 4, 6, 8} // 这里做了两件事，先创建了一个[2,4,6,8]的数组，然后又基于这个数组建立了对应的slice
		printSlice(s1)
	*/

	/*
		slice的创建方式四：
		- 许多情况下我们要建一个多大的slice，里面的值我们还不知道
		- 比如我们要建一个长度为10的slice，这时候我们要用到内建的函数make
	*/
	s1 := []int{2, 4, 6, 8}
	s2 := make([]int, 16)     // 建一个长度为16的int的slice
	s3 := make([]int, 10, 32) // 第三个参数可以指定cap的长度为32
	printSlice(s2)            // [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0],len=16,cap=16
	printSlice(s3)            // [0 0 0 0 0 0 0 0 0 0],len=10,cap=32

	/**
	slice的创建方式五：
	- 可以以拷贝的方式创建一个slice
	- 使用到内建函数copy
	*/
	copy(s2, s1)   // copy函数的入参需要注意一下，copy 第一个参数表示目标，第二个参数才是拷贝源，这里的意思是把s1切片的值拷贝给s2
	printSlice(s2) // [2 4 6 8 0 0 0 0 0 0 0 0 0 0 0 0] 因为len为10，所以其他坑位的int依然是零值，int的零值就是0

	/**
	【如何从slice中来删除一个element】
	- 这个系统中没有一个内建的函数来删除
	- 但是slice有很多slice功能，它非常好实现删除操作
	- 比如[2 4 6 8 0 0 0 0 0 0 0 0 0 0 0 0]想把8删了，那我们只需要找到8这个下标为3的删掉就好，s2[:3]+s2[4:]就可以实现了
	- 但是slice没有加法，所以没法两个slice使用+号，所以需要用append函数来做
	- append第二个参数是可变参数，要许多element（如append(slice,element1,element2)）
	- go支持在一个slice后边加三个点...来做到”解构“，这个在其他一些语言中也会有，但是很多是放在前边，go语言是放在后边的
	*/
	s2 = append(s2[:3], s2[:4]...) // append第二个参数是可变参数，要许多element（如append(slice,element1,element2)），但是也支持在一个slice后边加三个点...来做到”解构“，这个在其他一些语言中也会有，但是很多是放在前边，go语言是放在后边的
	fmt.Println(s2)

	/**
	- 从删除slice元素的使用习惯上来讲，我们删除中间的元素是不常见的
	- 而我们比较常见的删除slice中的一个元素一般是删除头或者尾
	*/
	// 删除头
	fmt.Printf("%v从头部删除了%v之后为%v\n", s2, s2[0], s2[1:])
	// 删除尾
	fmt.Printf("%v从尾部删除了%v之后为%v\n", s2, s2[len(s2)-1], s2[:len(s2)-1])
	// 删除头和尾
	fmt.Printf("%v删除了头和尾之后为%v", s2, s2[1:len(s2)-1])

}
